package de.fhkl.gatav.ut.bubbles;

import java.util.LinkedList;

import de.fhkl.gatav.ut.bubbles.R;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.SurfaceHolder;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

/****
 * bubblesView: Manages display handling of app. Implements SurfaceHolder.Callback to access certain display properties
 */
public class BubblesView extends SurfaceView implements SurfaceHolder.Callback {

	private SurfaceHolder surfaceHolder = null; //Surface
	private GameLoop gameLoop; //Display refresh thread

	private LinkedList<Bubble> bubbles = new LinkedList<Bubble>(); //Our bubble objects
	
	private float BUBBLE_FREQUENCY = 0.3f; // Bubble generation rate
	
	//Certain paint properties
	private Paint backgroundPaint = new Paint();
	private Bitmap bubbleBitmap;
	
	private static final String TAG = "BubblesView"; //For debugging purposes

/****
 * Constructor
 * @param context
 * @param attrs
 */
	public BubblesView(Context context, AttributeSet attrs) {
		super(context, attrs);
      
		getHolder().addCallback((Callback) this);
		backgroundPaint.setColor(Color.BLUE);
		bubbleBitmap = BitmapFactory.decodeResource(context.getResources(), 
    	            							    R.drawable.bubble);
	}

/****
 * drawScreen: Paints background and all bubbles	
 * @param c: Canvas to be drawn on
 */
	private void drawScreen(Canvas c) {
		//Draw background
		c.drawRect(0, 0, c.getWidth(), c.getHeight(), backgroundPaint);

		//Draw bubbles
		for (Bubble bubble : bubbles) {
			bubble.draw(c);
		}
	}

/****
 * calculateDisplay: Generates new bubble, moves bubble, removes unused bubbles	
 * @param canvas: Canvas to calculate moves for
 * @param numberOfFrames: No. of frames since last call
 */
	private void calculateDisplay(Canvas canvas, float numberOfFrames) {
		//Perhaps add a new bubble
		randomlyAddBubbles(canvas.getWidth(), canvas.getHeight(), numberOfFrames);

		//Move all bubbles and keep display leavers in mind
		LinkedList<Bubble> bubblesToRemove = new LinkedList<Bubble>();
		for (Bubble bubble : bubbles) {
			bubble.move(numberOfFrames);
			if (bubble.outOfRange())
				bubblesToRemove.add(bubble);
		}
		
		//Remove all bubbled up
		for (Bubble bubble : bubblesToRemove) {
			bubbles.remove(bubble);
		}
	}
 
/****
 * randomlyAddBubbles: Adds a bubble at random. Probability rises with the number of frames passed	
 * @param screenWidth ...
 * @param screenHeight ...
 * @param numFrames: No. of frames since last call
 */
	public void randomlyAddBubbles(int screenWidth, int screenHeight, float numFrames) {
		//Create a bubble every time the number of frame threshold is exceeded
		if (Math.random()>BUBBLE_FREQUENCY*numFrames)
			return;
      
		bubbles.add(new Bubble((int)(screenWidth*Math.random()),
							   screenHeight+Bubble.RADIUS,
							   (int)((Bubble.MAX_SPEED-0.1)*Math.random()+0.1),
							   bubbleBitmap));
	}
	
/****
 * Private display loop thread	
 */
	private class GameLoop extends Thread {
		private long msPerFrame = 1000/25;
		public boolean running = true;
		private long frameTime = 0;

		public void run() {
			Canvas canvas = null;
			long thisFrameTime;
			long lastFrameTime = System.currentTimeMillis();
			float framesSinceLastFrame;
			final SurfaceHolder surfaceHolder = BubblesView.this.surfaceHolder;
			frameTime = lastFrameTime;
			
			while (running) {
				try {
					canvas = surfaceHolder.lockCanvas();
					synchronized (surfaceHolder) {
						drawScreen(canvas);
					}
					thisFrameTime = System.currentTimeMillis();
//Later optimize to float					
					framesSinceLastFrame = (thisFrameTime - lastFrameTime)/msPerFrame;
//					framesSinceLastFrame = (float)(thisFrameTime - lastFrameTime)/msPerFrame;
					lastFrameTime = thisFrameTime;				
					calculateDisplay(canvas, framesSinceLastFrame);
				} finally {
					if (canvas != null)
						surfaceHolder.unlockCanvasAndPost(canvas);
				}
//Not needed in optimized (float) version				
				waitTillNextFrame();
			}
		}
		
		private void waitTillNextFrame() {
			long nextSleep = 0;
			frameTime += msPerFrame;
			nextSleep = frameTime - System.currentTimeMillis();
			if (nextSleep > 0) {
				try {
					sleep(nextSleep);
				} catch (InterruptedException e) {
				}
			}
		}
	}
		      			
/****
 * Interfcae implementation	
 */
	
/****
 * Called when display is up	
 */
	public void surfaceCreated(SurfaceHolder holder) {
		surfaceHolder = holder;

		//Start animation here		
		synchronized (this) {
		if (gameLoop == null) {
				gameLoop = new GameLoop();
				gameLoop.start();
			}
		}
	}
	
/****
 * Not used	
 */
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
		//Nothing to do
	}
	
/****
 * Called before display will be brouht down	
 */
	public void surfaceDestroyed(SurfaceHolder holder) {
		//Stop animation
		synchronized (this) {
			boolean retry = true;
			//Stop the loop
			if (gameLoop != null) {
				gameLoop.running = false;
				//Catch the thread
				while (retry) {
					try {
						gameLoop.join();
						retry = false;
					} catch (InterruptedException e) {
					}
				}
			}
			gameLoop = null;
		}
	}
}